#include "bsp_spi.h"
#include "W25Q64_Ins.h"
#include"W25Q64.h"
#include "OLED.h"
#include "cmsis_os.h"



void W25Q64_Init(void)
{
	MySPI_Init();
}

/**
 * @brief W25Q564芯片读取ID
 * 
 * @param MID 制造商ID（Manufacture ID） 
 * @param DID 设备ID （Device ID)
 */
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_JEDEC_ID);	/* 发送读ID指令 */
	*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	/* 只接收，交换随便发字节 */
	*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	*DID <<= 8;
	*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	MySPI_Stop();
}

/*
 ******************************************************************************
 ** 1. 16M = 256Block  1Block = 16Sector 1Sector = 16页 1页=256bytes
 ** 2. 最小擦除单位 : 1Sector = 4K     
 ******************************************************************************
 */

 
/*
**********************************************************
** desc : 读状态寄存器
** note : BIT 7   | 6  | 5  | 4   | 3  | 2   | 1  |  0
**            SPR | RV | TB | BP2 | BP1| BP0 | WEL|  BUSY
**        SPR : 默认0,状态寄存器保护位,配合WP使用
**        TB,BP2,BP1,BP0 : FLASH区域写保护设置
**        WEL : 写使能锁定
**        BUSY:忙标记位(1,忙;0,空闲)
**********************************************************
*/
static uint8_t W25Q64ReadSR(void)
{ 
    uint8_t Byte=0;   

    MySPI_Start();	
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1); //发送读取状态寄存器命令    
    Byte = MySPI_SwapByte(W25Q64_DUMMY_BYTE);          //读取一个字节  
    MySPI_Stop();	
    return Byte;   
} 

/*
**************************************************************
** desc : 写W25Q64状态寄存器
** note : 只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
**************************************************************
*/
static void W25Q64WriteSR(uint8_t SR)
{

    MySPI_Start();	
    MySPI_SwapByte(W25Q64_WRITE_STATUS_REGISTER);   //发送写取状态寄存器命令    
    MySPI_SwapByte(SR);                    //写入一个字节  
    MySPI_Stop();	
}   


/**
 * @brief W25Q64芯片写使能
 * 
 */
void W25Q64_WriteEnable(void)
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_WRITE_ENABLE);
	MySPI_Stop();
}

/**
 * @brief 每次芯片写入，擦除操作都需等待寄存器事件判断是否完成
 * 
 */
void W25Q64_WaitBusy(void)
{
	while((W25Q64ReadSR() & 0x01) == 0x01);	//等待BUSY位清空
}

/**
 * @brief 向w25q54芯片指定地址写入数据，注意如果写入地址超过该页的限制将从页首继续开始写
 * 
 * @param Address 芯片地址
 * @param Data 数据
 * @param len 数据长度
 */
void W25Q64_PageWrite(uint32_t Address, uint8_t *Data, uint16_t len)
{
	uint16_t i;
	
	W25Q64_WriteEnable();
	
	MySPI_Start();
	MySPI_SwapByte(W25Q64_PAGE_PROGRAM);	/* 写页面 */
	/* 选择地址（24位） */
	MySPI_SwapByte(Address >> 16);			/* 24位地址的[23:16] */
	MySPI_SwapByte(Address >> 8);			/* 24位地址的[15:8] */
	MySPI_SwapByte(Address);				/* 24位地址的[7:0] */
	
	/* 写入数据（每次写入1bit, 自动递增） */
	for (i = 0; i < len; i ++)
	{
		MySPI_SwapByte(Data[i]);
	}
	MySPI_Stop();
	
	W25Q64_WaitBusy();
}

/*
***************************************************************************
** desc : 无检验写SPI FLASH 
**        具有自动换页功能 
**        在指定地址开始写入指定长度的数据,但是要确保地址不越界!
** Buffer : 数据存储区
** WriteAddr : 开始写入的地址(24bit)
** WriteSize : 要写入的字节数(最大65535)
** note : 必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
***************************************************************************
*/
static void W25Q64PageWriteWithCross(uint32_t WriteAddr, uint8_t *Buffer, uint16_t WriteSize)
{
    uint16_t PageRemain = W25Q64_PAGE_SIZE - WriteAddr % W25Q64_PAGE_SIZE; //单页剩余的字节数		 	    

    if(WriteSize <= PageRemain)
        PageRemain = WriteSize;	//不大于256个字节

    while(1)
    {	
        W25Q64_PageWrite(WriteAddr,Buffer, PageRemain);
        if(WriteSize != PageRemain) 	//写入未结束
        {
            Buffer += PageRemain;
            WriteAddr += PageRemain; 
            
            WriteSize -= PageRemain;       //减去已经写入了的字节数
            PageRemain = (WriteSize > W25Q64_PAGE_SIZE) ? W25Q64_PAGE_SIZE : WriteSize;
        }
        else
        {
            break;  //写入结束了
        }
    }
} 

/**
 * @brief W25Q64写（按扇区，可跨页续写）
 * 
 * @param Address 
 * @param Data 
 * @param WriteSize 
 */
void W25Q64Write(uint32_t Address, uint8_t *Data, uint16_t WriteSize)
{
	static uint8_t sectorBuffer[W25Q64_SECTOR_SIZE];
	uint16_t sectorPos = Address / W25Q64_SECTOR_SIZE;		/* 扇区号 */
	uint16_t sectorOffset = Address % W25Q64_SECTOR_SIZE;	/* 在扇区内的地址偏移 */
	uint16_t sectorForWriteRemain = W25Q64_SECTOR_SIZE - sectorOffset;	/* 该扇区剩余可写的空间 */
	if(WriteSize <= sectorForWriteRemain){
		sectorForWriteRemain = WriteSize;
	}

	uint16_t i = 0;
	while(1){
		/* 读取一整个扇区 */
		W25Q64_ReadData(sectorPos * W25Q64_SECTOR_SIZE, sectorBuffer, W25Q64_SECTOR_SIZE);
		/* 校验扇区数据是否全已擦除 */
		for(i = 0; i < W25Q64_SECTOR_SIZE; i++)
		{
			if(sectorBuffer[i] != 0xFF)
				break;
		}

		if(i < W25Q64_SECTOR_SIZE){
			/* 存在写入情况，需擦除后再写入 */
			/* 擦除前先保存写入的数据 */
			for(i = 0; i < sectorForWriteRemain; i++){
				sectorBuffer[i + sectorOffset] = Data[i];
			}
			W25Q64_SectorErase(sectorPos * W25Q64_SECTOR_SIZE);
			/* 由于已经擦除数据，需写入整个扇区 */
			W25Q64PageWriteWithCross(Address, sectorBuffer, W25Q64_SECTOR_SIZE);
		}
		else{
			/* 无写入情况，直接在扇区余地址写 */
			W25Q64PageWriteWithCross(Address, sectorBuffer, sectorForWriteRemain);
		}

		if(WriteSize != sectorForWriteRemain){
			Data += sectorForWriteRemain;
			Address += sectorForWriteRemain;
			WriteSize -= sectorForWriteRemain;
			sectorPos++;
			sectorOffset = 0;
			sectorForWriteRemain = (WriteSize > W25Q64_SECTOR_SIZE) ? W25Q64_SECTOR_SIZE : WriteSize;
		}
		else{
			break;	/* 写入结束 */
		}
	}

}

/**
 * @brief 擦除整个芯片
 * 
 */
void W25Q64EraseChip(void)
{
    W25Q64_WriteEnable();                  
    MySPI_Start();  
    MySPI_SwapByte(W25Q64_CHIP_ERASE);     //发送片擦除命令  
    MySPI_Stop();	
    W25Q64_WaitBusy();   				    //等待芯片擦除结束
} 

/**
 * @brief 擦除一个扇区
 * 扇区地址 根据实际容量设置,第几个扇区
 * 擦除一个扇区的最少时间:150ms
 */
void W25Q64_SectorErase(uint32_t Address)
{
	W25Q64_WriteEnable();
	
	MySPI_Start();
	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);
	MySPI_SwapByte(Address >> 16);
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	MySPI_Stop();
	
	W25Q64_WaitBusy();	
}

/**
 * @brief W25Q64指定地址读
 * 
 * @param Address 24位地址
 * @param Data 数据buffer
 * @param len 长度
 */
void W25Q64_ReadData(uint32_t Address, uint8_t *Data, uint32_t len)
{
	uint32_t i;
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_DATA);
	MySPI_SwapByte(Address >> 16);
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	for (i = 0; i < len; i ++)
	{
		Data[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	}
	MySPI_Stop();
}


uint8_t MID;
uint16_t DID;

uint8_t ArrayWrite[] = {0xAA, 0xBB, 0xCC, 0xDD};
uint8_t ArrayRead[4];
void W25Q64TestTaskHandler(const void *pv)
{
	OLED_Init();
	W25Q64_Init();
	
	OLED_ShowString(1, 1, "MID:   DID:");
	OLED_ShowString(2, 1, "W:");
	OLED_ShowString(3, 1, "R:");
	
	W25Q64_ReadID(&MID, &DID);
	OLED_ShowHexNum(1, 5, MID, 2);
	OLED_ShowHexNum(1, 12, DID, 4);
	
	/* 写入数据前必须先擦除，擦除的最小单元是sector*/
	/* 擦除后，扇区所在所有数据变为FF */
	W25Q64_SectorErase(0x000000);	
	/* 向地址0000FE写入4bit数据0xAA 0xBB 0xCC 0xDD（块0，扇区0[xx0000h-xx0FFFH]） */
	W25Q64_PageWrite(0x0000FE, ArrayWrite, 4);
	
	/* 读出0x000000的数据，这里由于写越界，实际后面的2bit 0xCC 0xDD 会从地址0开始写,而0xAA 0xBB正常写入在0x0000FE和0x0000FF */
	W25Q64_ReadData(0x000000, ArrayRead, 4);
	
	OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);
	OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);
	OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);
	OLED_ShowHexNum(2, 12, ArrayWrite[3], 2);
	
	OLED_ShowHexNum(3, 3, ArrayRead[0], 2);
	OLED_ShowHexNum(3, 6, ArrayRead[1], 2);
	OLED_ShowHexNum(3, 9, ArrayRead[2], 2);
	OLED_ShowHexNum(3, 12, ArrayRead[3], 2);
	
	while (1)
	{
		
	}
}

void W25Q64Test()
{
	osThreadDef(W25Q64TestTask, W25Q64TestTaskHandler, osPriorityNormal, 0, 256);
	osThreadCreate(osThread(W25Q64TestTask), NULL);
}